package net.lcqf.websocket.server;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.epoll.Epoll;
import io.netty.channel.epoll.EpollEventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.util.SelfSignedCertificate;
import io.netty.util.concurrent.DefaultThreadFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.net.ssl.SSLException;
import java.security.cert.CertificateException;


/**
 * +----------------------------------------------------------------------
 * // | Talk To [ 聊来改善地区金融服务环境，助力企业发展 ]
 * // +----------------------------------------------------------------------
 * // | Copyright (c) 2021-2025 聊来 All rights reserved.
 * // +----------------------------------------------------------------------
 * // | Licensed ( <a href="http://www.apache.org/licenses/LICENSE-2.0">apache</a> )
 * // +----------------------------------------------------------------------
 * // | @Author: 聊来 <18970881148@qq.com>
 * // +----------------------------------------------------------------------
 * // | DateTime: 2025/4/15 16:44
 * // +----------------------------------------------------------------------
 */

@Component
public class WebSocketServer {

    private final Logger logger = LoggerFactory.getLogger(WebSocketServer.class);

    // 连接处理
    private EventLoopGroup bossGroup;
    // 事件处理
    private EventLoopGroup workerGroup;
    private Channel channel;

    private static final boolean SSL = System.getProperty("ssl") != null;

    public void start(int port) throws CertificateException, SSLException  {
        final SslContext sslContext = buildSslContext();
        try {
            boolean epoll = Epoll.isAvailable();

            int cpuNum = Runtime.getRuntime().availableProcessors();
            if(epoll){
                // Linux（内核≥2.6）
                bossGroup = new EpollEventLoopGroup(cpuNum);
                workerGroup = new EpollEventLoopGroup(cpuNum, new DefaultThreadFactory("epoll"));
            } else {
                bossGroup = new NioEventLoopGroup(1);
                workerGroup = new NioEventLoopGroup(cpuNum * 2, new DefaultThreadFactory("nio"));
            }
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.channel(NioServerSocketChannel.class);
            serverBootstrap.group(bossGroup, workerGroup);

            // 表示系统用于临时存放已完成三次握手的请求的队列的最大长度
            serverBootstrap.option(ChannelOption.SO_BACKLOG, 1024);
            // 是否开启TCP底层心跳机制，true为开启
            // 是否开启Nagle算法，true表示关闭，false表示开启（如果要求高实时性，有数据发送时就马上发送，就关闭，如果需要减少发送次数减少网络交互，就开启）
            serverBootstrap.childOption(ChannelOption.SO_KEEPALIVE, true).childOption(ChannelOption.TCP_NODELAY, true);
            serverBootstrap.childHandler(new WebSocketServerInitializer(sslContext));

            // 启动服务，监听端口 sync同步操作，如果设置成异步操作，就不会等待绑定端口完成，就执行后续代码
            channel = serverBootstrap.bind(port).channel();
//            channel.closeFuture().sync();
            logger.info("Netty WebSocket Server启动成功");
        } catch (Exception e) {
            logger.error("Netty WebSocket Server启动错误", e);
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
    public void close() {
        logger.info("Netty WebSocket Server关闭");
        if (channel != null) {
            channel.close();
        }

        bossGroup.shutdownGracefully();
        workerGroup.shutdownGracefully();
    }

    private static SslContext buildSslContext() throws CertificateException, SSLException {
        if (!SSL) { return null; }
        SelfSignedCertificate ssc = new SelfSignedCertificate();
        return SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build();
    }
}
